package com.ssd.handler.alipay;


import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.ssd.annotation.PayChannel;
import com.ssd.api.payment.dto.enums.PayChannelEnum;
import com.ssd.api.payment.dto.enums.RefundStatusEnum;
import com.ssd.api.payment.dto.enums.TradingStateEnum;
import com.ssd.constant.TradingConstant;
import com.ssd.enums.TradingEnum;
import com.ssd.exception.LogicException;
import com.ssd.handler.BasicPayHandler;
import com.ssd.model.domain.RefundRecord;
import com.ssd.model.domain.Trading;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 支付宝基础支付功能的实现
 * AliBasicPayHandler类是处理支付宝基础支付功能的组件
 * 该类通过Spring框架的@Component注解标识为一个组件，以便进行自动检测和注册
 * 同时，使用@PayChannel注解指定其处理的支付渠道为支付宝，以便在不同的支付场景下进行区分和调用
 * @author cy
 * @version 1.0
 */
@Slf4j
@Component("aliBasicPayHandler")
@PayChannel(type = PayChannelEnum.ALI_PAY)
public class AliBasicPayHandler implements BasicPayHandler {

    /**
     * 重写queryTrading方法以查询交易状态
     * @param trading 交易对象，包含查询所需的信息
     * @return 返回一个布尔值，表示查询是否成功
     * @throws LogicException 如果查询失败，抛出此异常
     */
    @Override
    public Boolean queryTrading(Trading trading) throws LogicException {
        //查询配置
        Config config = AlipayConfig.getConfig(trading.getEnterpriseId());
        //Factory使用配置（阿里提供的）
        Factory.setOptions(config);
        AlipayTradeQueryResponse queryResponse;
        try {
            //调用支付宝API：通用查询支付情况：根据交易号请求支付宝查询
            queryResponse = Factory
                    .Payment
                    .Common()
                    .query(String.valueOf(trading.getTradingOrderNo()));
        } catch (Exception e) {
            //格式化日志消息并抛出自定义异常
            String msg = StrUtil.format("查询支付宝统一下单失败：trading = {}", trading);
            log.error(msg, e);
            throw new LogicException( TradingEnum.NATIVE_QUERY_FAIL.getValue());//查询统一下单交易失败
        }

        //更新交易单状态
        trading.setTransactionId(queryResponse.getTradeNo());//第三方支付的交易号
        trading.setResultCode(queryResponse.getSubCode());//第三方交易返回编码【最终确认交易结果】
        trading.setResultMsg(queryResponse.getSubMsg());//第三方交易返回提示消息【最终确认交易信息】
        trading.setResultJson(JSONUtil.toJsonStr(queryResponse));//第三方交易返回信息json【分析交易最终信息】

        //检查响应是否成功
        boolean success = ResponseChecker.success(queryResponse);
        //响应成功，分析交易状态
        if (success) {
            //获取交易状态
            String tradeStatus = queryResponse.getTradeStatus();
            //根据交易状态更新交易状态枚举：未付款交易超时关闭，或支付完成后全额退款
            if (StrUtil.equals(TradingConstant.ALI_TRADE_CLOSED, tradeStatus)) {
                //支付取消：TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
                trading.setTradingState(TradingStateEnum.QXDD);//设置支付取消
            } else if (StrUtil.equalsAny(tradeStatus, TradingConstant.ALI_TRADE_SUCCESS, TradingConstant.ALI_TRADE_FINISHED)) {
                // TRADE_SUCCESS（交易支付成功）
                // TRADE_FINISHED（交易结束，不可退款）
                trading.setTradingState(TradingStateEnum.YJS);//设置已付款状态
            } else {
                //非最终状态不处理，当前交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）不处理
                return false;
            }
            return true;
        }
        //如果响应不成功，抛出异常
        throw new LogicException( TradingEnum.NATIVE_QUERY_FAIL.getValue());//查询统一下单交易失败
    }

    /**
     * 关闭交易
     * 本方法用于调用支付宝API来关闭一笔交易，确保交易状态得到正确更新
     * @param trading 交易对象，包含交易相关信息
     * @return 如果成功关闭交易，返回true；否则返回false
     * @throws LogicException 当关闭交易过程中发生错误时抛出
     */
    @Override
    public Boolean closeTrading(Trading trading) throws LogicException {
        //查询配置
        Config config = AlipayConfig.getConfig(trading.getEnterpriseId());
        //Factory使用配置（阿里提供的）
        Factory.setOptions(config);
        try {
            //调用支付宝API：通用查询支付情况
            AlipayTradeCloseResponse closeResponse = Factory
                    .Payment
                    .Common()
                    .close(String.valueOf(trading.getTradingOrderNo()));//交易号
            //检查关闭交易是否成功
            boolean success = ResponseChecker.success(closeResponse);
            if (success) {
                //更新交易状态为取消订单
                trading.setTradingState(TradingStateEnum.QXDD);
                return true;
            }
            return false;
        } catch (Exception e) {
            //当交易关闭失败时，抛出自定义异常
            throw new LogicException( TradingEnum.CLOSE_FAIL.getValue());//关闭交易单失败
        }
    }

    /**
     * 退款交易方法
     * 该方法用于处理支付宝的退款请求，根据传入的退款记录查询相应的配置，
     * 并使用支付宝的API进行退款操作
     * @param refundRecord 退款记录对象，包含退款所需的信息
     * @return 返回一个布尔值，表示退款是否成功
     * @throws LogicException 如果退款过程中发生错误，抛出此异常
     */
    @Override
    public Boolean refundTrading(RefundRecord refundRecord) throws LogicException {
        //查询配置
        Config config = AlipayConfig.getConfig(refundRecord.getEnterpriseId());
        //Factory使用配置（阿里提供的）
        Factory.setOptions(config);
        //调用支付宝API：通用查询支付情况
        AlipayTradeRefundResponse refundResponse;
        try {
            // 支付宝easy sdk
            refundResponse = Factory
                    .Payment
                    .Common()
                    //扩展参数：退款单号
                    .optional("out_request_no", refundRecord.getRefundNo())
                    .refund(Convert.toStr(refundRecord.getTradingOrderNo()),//交易号
                            Convert.toStr(refundRecord.getRefundAmount()));//本次退款金额
        } catch (Exception e) {
            //记录错误日志，并抛出自定义异常
            String msg = StrUtil.format("调用支付宝退款接口出错！refundRecord = {}", refundRecord);
            log.error(msg, e);
            throw new LogicException( msg);
        }
        //设置退款记录的退款ID为null，表示退款操作尚未完成
        refundRecord.setRefundId(null);//第三方支付的退款单号
        //设置退款记录的退款代码和消息
        refundRecord.setRefundCode(refundResponse.getSubCode());//返回编码
        refundRecord.setRefundMsg(JSONUtil.toJsonStr(refundResponse));//返回信息
        //检查退款响应是否成功
        boolean success = ResponseChecker.success(refundResponse);
        if (success) {
            //如果成功，更新退款记录的状态为成功，并返回true
            refundRecord.setRefundStatus(RefundStatusEnum.SUCCESS);
            return true;
        }
        //如果失败，抛出自定义异常，表示退款失败
        throw new LogicException( TradingEnum.NATIVE_REFUND_FAIL.getValue());
    }

    /**
     * 查询退款交易状态
     * 通过调用支付宝接口检查退款交易的状态，并更新退款记录中的相关信息
     *
     * @param refundRecord 退款记录对象，包含退款交易的相关信息
     * @return 如果退款成功，返回true；否则抛出异常
     * @throws LogicException 如果查询配置失败或支付宝接口调用失败，抛出此异常
     */
    @Override
    public Boolean queryRefundTrading(RefundRecord refundRecord) throws LogicException {
        //查询配置
        Config config = AlipayConfig.getConfig(refundRecord.getEnterpriseId());
        //Factory使用配置（阿里提供的）
        Factory.setOptions(config);

        AlipayTradeFastpayRefundQueryResponse response;
        try {
            //调用支付宝接口查询退款交易状态
            response = Factory.Payment.Common().queryRefund(
                    Convert.toStr(refundRecord.getTradingOrderNo()),//交易单
                    Convert.toStr(refundRecord.getRefundNo()));//退款单号
        } catch (Exception e) {
            //如果调用支付宝接口出错，记录错误日志并抛出异常
            log.error("调用支付宝查询退款接口出错！refundRecord = {}", refundRecord, e);
            throw new LogicException( TradingEnum.NATIVE_REFUND_FAIL.getValue());
        }

        //更新退款记录中的退款代码和消息
        refundRecord.setRefundCode(response.getSubCode());//返回编码
        refundRecord.setRefundMsg(JSONUtil.toJsonStr(response));//返回信息

        //检查响应是否表示成功
        boolean success = ResponseChecker.success(response);
        if (success) {
            //如果退款成功，更新退款状态并返回true
            refundRecord.setRefundStatus(RefundStatusEnum.SUCCESS);
            return true;
        }

        //如果退款不成功，抛出异常
        throw new LogicException( TradingEnum.NATIVE_REFUND_FAIL.getValue());
    }
}
